/*
 *    Copyright (c) 2018-2025, boss All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the following disclaimer.
 * Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution.
 * Neither the name of the boss developer nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 * Author: boss
 */
package com.hopes.bizz.poll.alarm.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hopes.bizz.common.core.util.DateUtils;
import com.hopes.bizz.poll.alarm.bo.AlarmClassificationConfig;
import com.hopes.bizz.poll.alarm.bo.AlarmLevel;
import com.hopes.bizz.poll.alarm.entity.PollAlarmConfigEntity;
import com.hopes.bizz.poll.alarm.entity.PollAlarmExceptionEntity;
import com.hopes.bizz.poll.alarm.entity.PollAlarmHandleRecordEntity;
import com.hopes.bizz.poll.alarm.mapper.PollAlarmExceptionMapper;
import com.hopes.bizz.poll.alarm.message.AlarmMessageDistributor;
import com.hopes.bizz.poll.alarm.service.PollAlarmConfigService;
import com.hopes.bizz.poll.alarm.service.PollAlarmExceptionService;
import com.hopes.bizz.poll.alarm.service.PollAlarmHandleRecordService;
import com.hopes.bizz.poll.common.constant.AlarmConstant;
import com.hopes.bizz.poll.common.constant.OmConstant;
import com.hopes.bizz.poll.common.key.RedisKey;
import com.hopes.bizz.poll.monitor.entity.PollMonitorFactorEntity;
import com.hopes.bizz.poll.monitor.entity.PollMonitorSiteEntity;
import com.hopes.bizz.poll.monitor.service.PollMonitorFactorService;
import com.hopes.bizz.poll.monitor.service.PollMonitorSiteService;
import com.hopes.bizz.poll.om.bo.OmData;
import com.hopes.bizz.poll.om.entity.PollRtDataEntity;
import com.hopes.bizz.poll.om.service.PollRtDataService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 异常报警
 *
 * @author daijie
 * @date 2023-09-21 15:56:06
 */
@Service
public class PollAlarmExceptionServiceImpl extends ServiceImpl<PollAlarmExceptionMapper, PollAlarmExceptionEntity> implements PollAlarmExceptionService {
    @Autowired
    private PollAlarmConfigService configService;
    @Autowired
    @Lazy
    private PollMonitorSiteService pollMonitorSiteService;
    @Autowired
    @Lazy
    private PollRtDataService pollRtDataService;
    @Autowired
    @Lazy
    private PollAlarmHandleRecordService handleRecordService;
    @Autowired
    private StringRedisTemplate redisTemplate;
    @Autowired
    private PollMonitorFactorService pollMonitorFactorService;

    /**
     * 数据异常的判定时间，单位min
     */
    private static final Integer EXCEPTION_TIME = 5;

    @Override
    public Boolean alarmExceptionJobHandler() {
        // step.1 查询全部正在使用的站点
        LambdaQueryWrapper<PollMonitorSiteEntity> wrapper = Wrappers.lambdaQuery();
        wrapper.eq(PollMonitorSiteEntity::getIsUse, true);
        wrapper.eq(PollMonitorSiteEntity::getIsOnline, true);
        List<PollMonitorSiteEntity> monitorSites = pollMonitorSiteService.list(wrapper);

        // step.2 查询分级报警全局配置
        LambdaQueryWrapper<PollAlarmConfigEntity> globalWrapper = Wrappers.lambdaQuery();
        globalWrapper.eq(PollAlarmConfigEntity::getDefaultFlag, true);
        PollAlarmConfigEntity globalConfig = configService.getOne(globalWrapper);

        Date now = new Date();
        for (PollMonitorSiteEntity monitorSite : monitorSites) {
            // step.3 查询当前站点是否有配置
            LambdaQueryWrapper<PollAlarmConfigEntity> configWrapper = Wrappers.lambdaQuery();
            configWrapper.eq(PollAlarmConfigEntity::getMonitorSiteId, monitorSite.getId());
            PollAlarmConfigEntity config = configService.getOne(configWrapper);

            // step.4 查询当前实时数据
            LambdaQueryWrapper<PollRtDataEntity> rtDataWrapper = Wrappers.lambdaQuery();
            rtDataWrapper.eq(PollRtDataEntity::getMonitorSiteId, monitorSite.getId());
            rtDataWrapper.eq(PollRtDataEntity::getDataType, OmConstant.DATA_TYPE_RT);
            PollRtDataEntity rtData = pollRtDataService.getOne(rtDataWrapper);

            // step.5 查询全部规则并按照级别从小到大排序
            List<AlarmLevel> alarmLevels;
            AlarmClassificationConfig netConfig;
            if (config == null || config.getNetConfig() == null) {
                netConfig = globalConfig.getNetConfig();
            } else if (!config.getNetConfig().getAlarmFlag()) {
                continue;
            } else {
                netConfig = config.getNetConfig();
            }
            alarmLevels = netConfig.getAlarmLevels().stream().filter(item->item.getAlarmLevel() != null)
                    .sorted(Comparator.comparing(AlarmLevel::getAlarmLevel)).collect(Collectors.toList());

            // 没有实时数据
            if (rtData == null) {
                continue;
            }

            JSONObject dataJson = JSONObject.parseObject(rtData.getDataJson().toString());
            PollMonitorFactorEntity param = new PollMonitorFactorEntity();
            param.setMonitorSiteId(monitorSite.getId());
            List<PollMonitorFactorEntity> monitorFactorEntityList = pollMonitorFactorService.list(Wrappers.query(param));

            for (PollMonitorFactorEntity factor : monitorFactorEntityList) {
                if (!factor.getAlarmFlag()) {
                    continue;
                }
                String factorCode = factor.getFactorCode();
                OmData omData = dataJson.getObject(factorCode, OmData.class);
                if (omData == null) {
                    continue;
                }
                BigDecimal val = omData.getVal();
                if (val == null) {
                    continue;
                }

                String key = RedisKey.ALARM_EXCEPTION_DATE + monitorSite.getId() + "_" + factorCode + "_" + val;
                if (Boolean.FALSE.equals(redisTemplate.hasKey(key))) {
                    redisTemplate.opsForValue().set(key, String.valueOf(rtData.getDataTime().getTime()), EXCEPTION_TIME, TimeUnit.MINUTES);
                    clearAlarmNetIfExist(monitorSite, omData);
                    continue;
                } else {
                    // 如果最新的数据时间和redis里存放的时间是一样的，那么就不是后续上来的数据
                    if (String.valueOf(rtData.getDataTime().getTime()).equals(redisTemplate.opsForValue().get(key))) {
                        clearAlarmNetIfExist(monitorSite, omData);
                        continue;
                    } else {
                        redisTemplate.expire(key, EXCEPTION_TIME, TimeUnit.MINUTES);
                    }
                }

                AlarmLevel finalLevel = null;
                Long min = DateUtils.getIntervalMin(
                        DateUtils.getTimeStrByMilliSecond(Long.parseLong(Objects.requireNonNull(redisTemplate.opsForValue().get(key)))), now);

                for (AlarmLevel alarmLevel : alarmLevels) {
                    if (alarmLevel.getLeft() < min) {
                        finalLevel = alarmLevel;
                    }
                }

                if (finalLevel == null) {
                    clearAlarmNetIfExist(monitorSite, omData);
                    continue;
                }

                saveOrUpdateAlarmException(monitorSite, omData, now, min, finalLevel, val);
            }
        }
        return true;
    }

    private void saveOrUpdateAlarmException(PollMonitorSiteEntity monitorSite, OmData omData,
                                      Date date, Long min, AlarmLevel alarmLevel, BigDecimal monitorValue) {
        // 是否已经有报警
        LambdaQueryWrapper<PollAlarmExceptionEntity> alarmWrapper = Wrappers.lambdaQuery();
        alarmWrapper.eq(PollAlarmExceptionEntity::getMonitorSiteId, monitorSite.getId());
        alarmWrapper.eq(PollAlarmExceptionEntity::getAlarmStatus, AlarmConstant.ALARM_STATUS_NOT);
        alarmWrapper.eq(PollAlarmExceptionEntity::getHandleStatus, AlarmConstant.ALARM_HANDLE_STATUS_NOT);
        alarmWrapper.eq(PollAlarmExceptionEntity::getFactorCode, omData.getKey());
        alarmWrapper.orderByDesc(PollAlarmExceptionEntity::getAlarmTime);
        PollAlarmExceptionEntity exist = this.getOne(alarmWrapper, false);

        if (exist != null) {
            Integer handleStatus = exist.getHandleStatus();
            if (handleStatus == AlarmConstant.ALARM_HANDLE_STATUS_NOT) {
                // 有则更新持续时间和报警级别
                exist.setDurationTime(min);
                exist.setAlarmLevel(alarmLevel.getAlarmLevel());
                exist.setAlarmContent(buildAlarmContent(exist.getAlarmTimeStr(),
                        monitorSite.getName(), exist.getFactorCodeStr(), monitorValue));

                this.updateById(exist);
                return;
            } else if (handleStatus == AlarmConstant.ALARM_HANDLE_STATUS_ALREADY){
                LambdaQueryWrapper<PollAlarmHandleRecordEntity> recordWrapper = Wrappers.lambdaQuery();
                recordWrapper.eq(PollAlarmHandleRecordEntity::getModuleCode, AlarmConstant.ALARM_TYPE_EXCEPTION);
                recordWrapper.eq(PollAlarmHandleRecordEntity::getAlarmId, exist.getId());
                recordWrapper.orderByDesc(PollAlarmHandleRecordEntity::getHandleTime);
                PollAlarmHandleRecordEntity record = handleRecordService.getOne(recordWrapper, false);
                if (record.getHandMovement() && record.getDeadline().after(new Date())) {
                    return;
                }
            }
        }
        // 新造报警
        PollAlarmExceptionEntity alarmEntity = new PollAlarmExceptionEntity();
        alarmEntity.setAlarmTime(date);
        alarmEntity.setDurationTime(min);
        alarmEntity.setAlarmLevel(alarmLevel.getAlarmLevel());
        alarmEntity.setAlarmStatus(AlarmConstant.ALARM_STATUS_NOT);
        alarmEntity.setHandleStatus(AlarmConstant.ALARM_HANDLE_STATUS_NOT);
        alarmEntity.setMonitorSiteId(monitorSite.getId());
        alarmEntity.setOrgId(monitorSite.getOrgId());
        alarmEntity.setFactorCode(omData.getKey());
        alarmEntity.setMonitorValue(monitorValue);

        alarmEntity.setAlarmContent(buildAlarmContent(alarmEntity.getAlarmTimeStr(),
                monitorSite.getName(), alarmEntity.getFactorCodeStr(), monitorValue));

        this.save(alarmEntity);

        AlarmMessageDistributor.broadcastMessage(alarmEntity.getAlarmContent());
    }

    private void clearAlarmNetIfExist(PollMonitorSiteEntity monitorSite, OmData omData) {
        // 是否已经有报警
        LambdaQueryWrapper<PollAlarmExceptionEntity> alarmWrapper = Wrappers.lambdaQuery();
        alarmWrapper.eq(PollAlarmExceptionEntity::getMonitorSiteId, monitorSite.getId());
        alarmWrapper.eq(PollAlarmExceptionEntity::getAlarmStatus, AlarmConstant.ALARM_STATUS_NOT);
        alarmWrapper.eq(PollAlarmExceptionEntity::getFactorCode, omData.getKey());
        alarmWrapper.orderByDesc(PollAlarmExceptionEntity::getAlarmTime);

        PollAlarmExceptionEntity exist = this.getOne(alarmWrapper, false);

        // 消除报警
        if (exist != null) {
            exist.setAlarmStatus(AlarmConstant.ALARM_STATUS_ALREADY);
            exist.setRemoveTime(new Date());
            this.updateById(exist);
        }
    }

    private String buildAlarmContent(String date, String monitor, String factor, BigDecimal monitorValue) {
        // [数据异常报警]，开始时间[var]，点位[var]，报警因子[var]，监测值为[var]，特此通知!
        StringBuilder sb = new StringBuilder("[数据异常报警]");
        sb.append("开始时间:").append(date)
                .append(", 点位:").append(monitor)
                .append(", 报警因子:").append(factor);
        sb.append(", ").append("监测值为:").append(monitorValue).append(", ");
        sb.append(", 特此通知!");
        return sb.toString();
    }
}